//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#ifndef KNOWLEDGEREPOSITORY_H_
#define KNOWLEDGEREPOSITORY_H_

#include "INETDefs.h"

//#include "OLSR_HelloPiggybacking.h"
#include "OLSR.h"
//#include "aodv_uu_omnet.h"
#include "ManetRoutingCallable.h"
#include "TypeDefinitions.h"

#include "PersonalStatusUpdate_m.h"
#include "OLSRpkt_m.h"

typedef struct StimulusStruct{
    simtime_t timestamp;
    IPvXAddress ipSrc;
    double stimulus;
    double energyFator;
    double congestionFactor;
    int nHop;

    friend std::ostream& operator<<(std::ostream& os, const StimulusStruct& o)
    {
        os << o.ipSrc << " -> " << o.stimulus << " (E: " << o.energyFator << " C: " << o.congestionFactor << " nHop: " << o.nHop << ")";
        return os;
    }
} Stimulus_t;

typedef struct GatewayStruct{
    simtime_t timestamp;
    IPvXAddress ipSrc;
    bool isON;
    double energyFator;
    double energyResidual;
    unsigned int nHop;

    friend std::ostream& operator<<(std::ostream& os, const GatewayStruct& o)
    {
        os << o.ipSrc << " -> " << (o.isON ? "ON" : "OFF")<< " (E: " << o.energyFator << " ER: " << o.energyResidual << " nHop: " << o.nHop << ")";
        return os;
    }
} Gateway_t;

class INET_API KnowledgeRepository : public cSimpleModule
{
public:
    KnowledgeRepository();
    virtual ~KnowledgeRepository();

    float getCongestionFactorCell() const;
    void setCongestionFactorCell(float congestionFactorCell);

    float getCongestionFactorWifi() const;
    void setCongestionFactorWifi(float congestionFactorWifi);

    float getEnergyFactor() const;
    void setEnergyFactor(float energyFactor);

    STEM_ROLE_t getActualRule() const;
    void setActualRule(STEM_ROLE_t actualRule);

    STEM_ROLE_t getRealActualRule();

    void setStimulusIntervalTime(simtime_t stimulusTime);
    void setGatewayIntervalTime(simtime_t gatewayTime);
    void setStimulusOnRouting(bool stimOnRouting);

    float getAverageGatewayDistance(void);
    int getSpecificGatewayDistance(IPvXAddress ipGW);

    void getNeighbourhoodInfoList(std::list<node_state_info_t> &neigh_list);

    void updateFromHello(node_state_info_t info);
    void updateFromStimulus(IPvXAddress ipSrc, double stimulus, double energy, double congestion, int nHopCount);
    void updateFromGatewayMsg(IPvXAddress ipSrc, bool gwON, double energy, double energyResidual, unsigned int nHopCount);

    void getNeighIPlist(std::list<IPvXAddress> &list);

    IPvXAddress getNearestGateway(void);
    int getHopNearestGateway(void);

    float getMaxEnergyFactor();
    float getMaxCongestionFactorWifi();
    float getMaxCongestionFactorCell();
    float getMaxGatewayDistance();

    float getMaxNeighStimulus(void);
    float getAverageNeighStimulus(void);

    int getTotalGWinNet(void);

    void setAlphaVar(double alphaVar) { alpha_var = alphaVar; }
    void setBetaVar(double betaVar) { beta_var = betaVar; }
    void setResponseExponentVar(int responseExponentVar) { responseExponent = responseExponentVar; }
    void setTotalResponseExponentVar(int totResponseExponentVar) { totalResponseExponent = totResponseExponentVar; }

    bool checkRREQforward(double route_stimulus);
    double getRoutingStimulus(int attemptsCount);

protected:
    virtual int numInitStages() const {return 1;}
    virtual void initialize(int stage);
    virtual void handleMessage(cMessage *msg);
    virtual void finish();

    virtual void handleSelfMsg(cMessage *msg);
    virtual void handlePolicyMsg(PersonalStatusUpdate *msg);

    virtual void updateDB(void);

private:

    float getAverageGatewayDistance_OLSR(OLSR *olsrModule);
    //float getAverageGatewayDistance_AODV(AODVUU *aodvModule);
    float getAverageGatewayDistance_generic(void);

    void getNeighbourhoodInfoList_OLSR(std::list<node_state_info_t> &neigh_list, OLSR *olsrModule);
    //void getNeighbourhoodInfoList_AODV(std::list<node_state_info_t> &neigh_list, AODVUU *aodvModule);
    void getNeighbourhoodInfoList_generic(std::list<node_state_info_t> &neigh_list);

    IPvXAddress getNearestGateway_OLSR(OLSR *olsrModule);
    //IPvXAddress getNearestGateway_AODV(AODVUU *aodvModule);
    IPvXAddress getNearestGateway_generic(void);

    void getNeighIPlist_OLSR(std::list<IPvXAddress> &list, OLSR *olsrModule);
    //void getNeighIPlist_AODV(std::list<IPvXAddress> &list, AODVUU *aodvModule);
    void getNeighIPlist_generic(std::list<IPvXAddress> &list);

    void graphicUpdate(void);

    double calculateRouteThreshold(void);

protected:
    /** @name gate from brain */
    /*@{*/
    int brainIn;
    /*@}*/

    /** @name gate to brain */
    /*@{*/
    int brainOut;
    /*@}*/

    /** @name gate from policy */
    /*@{*/
    int policyIn;
    /*@}*/

    /** @name gate to policy */
    /*@{*/
    int policyOut;
    /*@}*/

    /** @name gate from policy */
    /*@{*/
    int coopIn;
    /*@}*/

    /** @name gate to policy */
    /*@{*/
    int coopOut;
    /*@}*/

private:

    // actual personal info (set by Policy manager)
    float energy_factor;
    float congestion_factor_wifi;
    float congestion_factor_cell;
    STEM_ROLE_t actual_rule;
    int responseExponent;
    int totalResponseExponent;

    double gamma_par;

    bool stimulusOnRouting;

    unsigned int maxGatewayHops;
    simtime_t dbTimeout;

    cMessage *msgUpdate;

    // info database
    std::list<NodeInfo_t> info_list;

    // stimulus DB
    std::map<IPvXAddress, Stimulus_t> stimulusMap;
    simtime_t stimulusIntervalTime;

    // gateway DB
public:
    ManetRoutingCallable *manet;
    std::map<IPvXAddress, Gateway_t> gatewayMap;
private:
    simtime_t gatewayIntervalTime;

    double alpha_var;
    double beta_var;

};

#endif /* KNOWLEDGEREPOSITORY_H_ */
